﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MatchmakingActioner.cs" company="DissingNelleman">
//   
// All rights reserved. </copyright>
// <summary>
//   The matchmaking actioner.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace ServiceLibrary.Actioners.Activities
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using NHibernate;
    using NHibernate.Criterion;

    using ServiceLibrary.Models.Base;
    using ServiceLibrary.Models.Domain;
    using ServiceLibrary.Services.Commands;
    using ServiceLibrary.Services.Queries;
    using ServiceLibrary.Services.Results;

    /// <summary>
    /// The matchmaking actioner.
    /// </summary>
    internal class MatchmakingActioner : ActionerBase
    {
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="MatchmakingActioner"/> class.
        /// </summary>
        /// <param name="session">
        /// The session.
        /// </param>
        /// <param name="activeUser">
        /// The active user.
        /// </param>
        public MatchmakingActioner(ISession session, User activeUser)
            : base(session, activeUser)
        {
        }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// The create matchmaking.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <returns>
        /// The <see cref="Guid"/>.
        /// </returns>
        public Guid CreateMatchmaking(Matchmaking obj)
        {
            obj.CreatedDateTime = DateTime.Now;
            this.Session.Save(obj);
            this.Log.CreateLogEntry(
                obj.ID, 
                string.Format("Matchmaking '{0}' blev oprettet.", obj.Name), 
                Enums.LogEntryType.Matchmaking
                );

            return obj.ID;
        }

        /// <summary>
        /// The create matchmaking.
        /// </summary>
        /// <param name="cmd">
        /// The cmd.
        /// </param>
        public void CreateMatchmaking(CreateMatchmakingCommand cmd)
        {
            if (cmd.NewMatchmaking != null)
            {
                var obj = new Matchmaking
                              {
                                  ID = cmd.NewMatchmaking.ID, 
                                  Name = cmd.NewMatchmaking.Name, 
                                  Buyers = cmd.NewMatchmaking.Buyers, 
                                  TimeOfActivity = cmd.NewMatchmaking.Date, 
                                  Comments = cmd.NewMatchmaking.Comments, 
                                  FundsText = cmd.NewMatchmaking.FundsText, 
                                  Location = this.Session.Get<Location>(cmd.NewMatchmaking.Location), 
                              };

                if (cmd.NewMatchmaking.OwnerProjects != null && cmd.NewMatchmaking.OwnerProjects.Any())
                {
                    obj.OwnerProjects = new HashSet<Project>();
                    foreach (Guid prID in cmd.NewMatchmaking.OwnerProjects)
                    {
                        var project = this.Session.Get<Project>(prID);
                        if (project != null)
                        {
                            obj.OwnerProjects.Add(project);
                        }
                    }
                }

                if (cmd.NewMatchmaking.Profiles != null && cmd.NewMatchmaking.Profiles.Any())
                {
                    obj.Profiles = new HashSet<Profile>();
                    foreach (Guid prID in cmd.NewMatchmaking.Profiles)
                    {
                        var profile = this.Session.Get<Profile>(prID);
                        if (profile != null)
                        {
                            obj.Profiles.Add(profile);
                        }
                    }
                }

                if (cmd.NewMatchmaking.Funds != null && cmd.NewMatchmaking.Funds.Any())
                {
                    foreach (Guid id in cmd.NewMatchmaking.Funds)
                    {
                        var fund = this.Session.Get<Fund>(id);
                        if (fund != null)
                        {
                            obj.Funds.Add(fund);
                        }
                    }
                }

                if (cmd.NewMatchmaking.OtherFinancing != null && cmd.NewMatchmaking.OtherFinancing.Any())
                {
                    foreach (Guid id in cmd.NewMatchmaking.OtherFinancing)
                    {
                        var fund = this.Session.Get<Fund>(id);
                        if (fund != null)
                        {
                            obj.OtherFinancing.Add(fund);
                        }
                    }
                }

                if (cmd.NewMatchmaking.Partners != null && cmd.NewMatchmaking.Partners.Any())
                {
                    foreach (Guid id in cmd.NewMatchmaking.Partners)
                    {
                        var partner = this.Session.Get<MatchmakingPartner>(id);
                        if (partner != null)
                        {
                            obj.Partners.Add(partner);
                        }
                    }
                }

                CreateMatchmaking(obj);
            }
        }

        /// <summary>
        /// The execute search query.
        /// </summary>
        /// <param name="matchmakingQuery">
        /// The matchmaking query.
        /// </param>
        /// <returns>
        /// The <see cref="IResult"/>.
        /// </returns>
        public IResult ExecuteSearchQuery(MatchmakingQuery matchmakingQuery)
        {
            SearchSettings settings = matchmakingQuery.Settings;
            var result = new MatchmakingResult();

            IQueryOver<Matchmaking, Matchmaking> search = this.Session.QueryOver<Matchmaking>();
            this.ApplyDefaultCriterias(settings, search);


            this.ApplyDefaultSearchFieldCriteriasReportsSearch(matchmakingQuery.DefaultFields, search);

            if (matchmakingQuery.AtLocation.HasValue && matchmakingQuery.AtLocation.Value != Guid.Empty)
            {
                search = search.Where(x => x.Location.ID == matchmakingQuery.AtLocation.Value);
            }

            if (matchmakingQuery.WithPartnerType.HasValue && matchmakingQuery.WithPartnerType.Value != Guid.Empty)
            {
                var partners =
                    Session.QueryOver<MatchmakingPartner>()
                           .Where(x => x.Type.ID == matchmakingQuery.WithPartnerType.Value);
                if (partners.RowCount() > 0)
                {
                    var partnerIDs = partners.List().Select(x => x.ID).ToList();

                    search.RootCriteria.CreateAlias("Partners", "mp")
                          .Add(Restrictions.In("mp.ID", partnerIDs));
                }
                else
                {
                    //If non ID's where found, then no partners has the wanted IDs, and therefore no result is valid.
                    search = search.Where(x => x.ID == Guid.NewGuid());
                }
            }

            if ((matchmakingQuery.ContainsFunds ?? new List<Guid>()).Any())
            {
                search.RootCriteria.CreateAlias("Funds", "funds")
                         .Add(Restrictions.In("funds.ID", matchmakingQuery.ContainsFunds));
            }

            if (matchmakingQuery.Profiles != null && matchmakingQuery.Profiles.Any())
            {
                search.RootCriteria.CreateAlias("Profiles", "prs")
                         .Add(Restrictions.In("prs.ID", matchmakingQuery.Profiles));
            }

            var profilesAreAtRemoteArea = matchmakingQuery.ProfilesAreAtRemoteArea ?? false;
            var profilesAreEntrepreneur = matchmakingQuery.ProfilesAreEntrepreneur ?? false;
            if (profilesAreAtRemoteArea || profilesAreEntrepreneur)
            {
                var searchProfiles = this.Session.QueryOver<Models.Domain.Profile>();

                if (profilesAreAtRemoteArea)
                {
                    searchProfiles = searchProfiles.Where(x => x.IsRemoteArea);
                }

                if (profilesAreEntrepreneur)
                {
                    searchProfiles = searchProfiles.Where(x => x.IsEntrepreneur);
                }

                var profileList = searchProfiles.List().Select(x => x.ID);

                if (profileList.Any())
                {
                    search.RootCriteria.CreateAlias("Profiles", "atprof")
                          .Add(Restrictions.In("atprof.ID", profileList.ToArray()));
                }
                else
                {
                    //No profiles match the search, force return 0 results
                    search = search.Where(x => x.ID == Guid.Empty);
                }
            }

            result.TotalRowCount = search.RowCount();
            var list = ApplySkipAndTake(settings, this.Sort(settings, search.List()));

            foreach (Matchmaking matchmaking in list)
            {
                result.List.Add(Services.Dto.Matchmaking.Convert(matchmaking));
            }

            return result;
        }

        /// <summary>
        /// The sort.
        /// </summary>
        /// <param name="settings">
        /// The settings.
        /// </param>
        /// <param name="search">
        /// The search.
        /// </param>
        /// <returns>
        /// The sorted list of matchmakings.
        /// </returns>
        private IOrderedEnumerable<Matchmaking> Sort(SearchSettings settings, IEnumerable<Matchmaking> search)
        {
            bool asc = settings.SortAscending == null || (bool)settings.SortAscending;

            if (settings.OrderBy != null)
            {
                switch (settings.OrderBy)
                {
                    case "Name":
                        return asc ? search.OrderBy(x => x.Name) : search.OrderByDescending(x => x.Name);
                }
            }

            return asc ? search.OrderBy(x => x.TimeOfActivity) : search.OrderByDescending(x => x.TimeOfActivity);
        }

        #endregion

        #region Methods

        /// <summary>
        /// The delete matchmaking.
        /// </summary>
        /// <param name="cmd">
        /// The cmd.
        /// </param>
        internal void DeleteMatchmaking(DeleteMatchmakingCommand cmd)
        {
            if (cmd.Dto != Guid.Empty)
            {
                var matchmaking = this.Session.Get<Matchmaking>(cmd.Dto);
                if (matchmaking != null)
                {
                    this.Log.CreateLogEntry(
                        matchmaking.ID,
                        string.Format("Matchmaking '{0}' blev slettet.", matchmaking.Name),
                        Enums.LogEntryType.Matchmaking,
                            Enums.LogEntryActionType.Delete
                        );

                    this.Session.Delete(matchmaking);
                }
            }
        }

        /// <summary>
        /// The update matchmaking.
        /// </summary>
        /// <param name="cmd">
        /// The cmd.
        /// </param>
        internal void UpdateMatchmaking(UpdateMatchmakingCommand cmd)
        {
            if (cmd.Dto != null)
            {
                var obj = this.Session.Get<Matchmaking>(cmd.Dto.ID);
                if (obj != null)
                {
                    obj.Name = cmd.Dto.Name;
                    obj.Buyers = cmd.Dto.Buyers;
                    obj.TimeOfActivity = cmd.Dto.Date;
                    obj.Comments = cmd.Dto.Comments;
                    obj.FundsText = cmd.Dto.FundsText;
                    obj.Location = this.Session.Get<Location>(cmd.Dto.Location);

                    if (cmd.Dto.OwnerProjects != null && cmd.Dto.OwnerProjects.Any())
                    {
                        obj.OwnerProjects.Clear();
                        foreach (Guid prID in cmd.Dto.OwnerProjects)
                        {
                            var project = this.Session.Get<Project>(prID);
                            if (project != null)
                            {
                                obj.OwnerProjects.Add(project);
                            }
                        }
                    }

                    if (cmd.Dto.Profiles != null && cmd.Dto.Profiles.Any())
                    {
                        obj.Profiles.Clear();
                        foreach (Guid prID in cmd.Dto.Profiles)
                        {
                            var profile = this.Session.Get<Profile>(prID);
                            if (profile != null)
                            {
                                obj.Profiles.Add(profile);
                            }
                        }
                    }

                    obj.Funds.Clear();
                    if (cmd.Dto.Funds != null && cmd.Dto.Funds.Any())
                    {
                        foreach (Guid id in cmd.Dto.Funds)
                        {
                            var fund = this.Session.Get<Fund>(id);
                            if (fund != null)
                            {
                                obj.Funds.Add(fund);
                            }
                        }
                    }

                    obj.OtherFinancing.Clear();
                    if (cmd.Dto.OtherFinancing != null && cmd.Dto.OtherFinancing.Any())
                    {
                        foreach (Guid id in cmd.Dto.OtherFinancing)
                        {
                            var fund = this.Session.Get<Fund>(id);
                            if (fund != null)
                            {
                                obj.OtherFinancing.Add(fund);
                            }
                        }
                    }

                    obj.Partners.Clear();
                    if (cmd.Dto.Partners != null && cmd.Dto.Partners.Any())
                    {
                        foreach (Guid id in cmd.Dto.Partners)
                        {
                            var partner = this.Session.Get<MatchmakingPartner>(id);
                            if (partner != null)
                            {
                                obj.Partners.Add(partner);
                            }
                        }
                    }

                    this.Session.Update(obj);
                    this.Log.CreateLogEntry(
                        obj.ID,
                        string.Format("Matchmaking '{0}' blev opdateret.", obj.Name),
                        Enums.LogEntryType.Matchmaking,
                            Enums.LogEntryActionType.Update
                        );
                }
            }
        }

        #endregion
    }
}